package javaLabs.lab1.ListQueue;

import java.util.*;



/**
 * Queue structure based on linked list.
 * Worked only with int/Integer.
 * Null values are not allowed!
 * @author Mixser
 */

public class ListQueue extends AbstractCollection<Integer> implements Queue<Integer> {

    /*
        Вспомогательные классы для для реализации структуры данных
    */

    // элемент списка

    /**
     * private realization of linked list
     */
    private class ListNode {

        private Integer value;
        private ListNode prev;
        private ListNode next;

        public void setPrev(ListNode prev) {
            this.prev = prev;
        }

        public void setNext(ListNode next) {
            this.next = next;
        }

        public void setValue(Integer value) {
            this.value = value;
        }

        public ListNode getPrev() {
            return prev;
        }

        public ListNode getNext() {
            return next;
        }

        public Integer getValue() {
            return value;
        }

        public ListNode(int value) {
            this.value = value;
            this.next = null;
            this.prev = null;
        }

        public ListNode(ListNode node) {
            if (node != null) {
                this.value = node.getValue();
                this.next = node.getNext();
                this.prev = node.getPrev();
            } else {
                this.value = null;
                this.next = null;
                this.prev = null;
            }
        }
    }

    /**
     * Implementation of iterator
     */
    private class ListQueueIterator implements Iterator<Integer> {

        private ListNode it = null;

        @Override
        public boolean hasNext() {
            return head != null && (it == null || it.getPrev() != null);
        }

        @Override
        public Integer next() {
            // мы ссылаемся уже куда-либо
            if (it != null) {
                it = it.getPrev();
                return it.getValue();
            } else {
                // мы ссылаемся на пустое место перед началом списка
                if (it == null && head != null) {
                    it = head;
                    return it.getValue();
                } else {
                    throw new NoSuchElementException("There are no elements in queue.");
                }
            }
        }

        @Override
        public void remove() {
            ListNode toDeleteNode = it != null ? new ListNode(it) : null;
            if (toDeleteNode != null) {
                // Если удаляем голову, то нужно пометить текущее местоположение как "пустое",
                // иначе просто переходим по списку
                if (it.equals(head)) {
                    it = null;
                } else {
                    it = it.getPrev();
                }
                removeNode(toDeleteNode);
            }
        }
    }

    private ListNode tail; // конец очереди
    private ListNode head; // начало очереди - самый первый элемент
    private int size; // количество элементов в очереди

    /**
     * Remove node from queue
     * @param node node which wanted to remove
     * @return <tt>true</tt> if node is removed else <tt>false</tt> if node is null
     */
    private boolean removeNode(ListNode node) {
        if (node == null) {
            return false;
        } else {
            if (node.getPrev() != null) {
                node.getPrev().setNext(node.getNext());
            } else {
                tail = node.getNext();
            }

            if (node.getNext() != null) {
                node.getNext().setPrev(node.getPrev());
            } else {
                head = node.getPrev();
            }

            --this.size;

            return true;
        }
    }

    // переопределенные методы от AbstractCollection<Integer> и Queue<Integer>

    /**
     * get queue's iterator
     * @return iterator
     */
    @Override
    public Iterator<Integer> iterator() {
        return new ListQueueIterator();
    }

    /**
     * get queue's size
     * @return size of queue
     */
    @Override
    public int size() {
        return this.size;
    }

    /**
     * insert element into queue
     * @param integer number, which you want to insert
     * @return <tt>true</tt> if element was inserted to queue, else <tt>false</tt>
     * @throws NullPointerException if element is null
     */
    @Override
    public boolean offer(Integer integer) {
        if (integer != null) {
            ListNode node = new ListNode(integer);
            ++this.size;
            if (head == null) {
                head = tail = node;
            } else {
                node.setNext(tail);
                tail.setPrev(node);
                tail = node;
                node.setPrev(null);
            }

            return true;
        } else {
            throw new NullPointerException("Null isn't supported!");
        }
    }

    /**
     * insert element into queue.
     * @param integer number, which you want to insert
     * @return true if element was inserted to queue, else false
     * @throws NullPointerException if element is null
     */
    @Override
    public boolean add(Integer integer) {
        if (integer != null) {
            return offer(integer);
        } else {
            throw new NullPointerException("Null isn't supported!");
        }
    }

    /**
     * Remove head of queue
     * @return head's value of queue
     * @throws NoSuchElementException if queue is empty
     */
    @Override
    public Integer remove() {
        if (head == null) {
            throw new NoSuchElementException("Queue is empty.");
        }
        return poll();
    }

    /**
     * Remove head of queue
     * @return head's value of queue or <tt>null</tt> if queue is empty
     */
    @Override
    public Integer poll() {
        if (head != null) {
            Integer value = head.getValue();
            removeNode(head);
            return value;
        } else {
            return null;
        }
    }

    /**
     * get head of queue without removing it
     * @return head's value of queue
     * @throws NoSuchElementException if queue is empty
     */
    @Override
    public Integer element() {
        if (head != null) {
            return head.getValue();
        } else {
            throw new NoSuchElementException("Queue is empty.");
        }
    }

    /**
     * get head of queue without removing it
     * @return head's value of queue, or <tt>null</tt> if queue is empty
     */
    @Override
    public Integer peek() {
        return head == null ? null : head.getValue();
    }
}